/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use crate::common::constants::*;
use crate::common::types::*;
use crate::ScheduleMode;
use crate::{Box, log_e, log_i, Vec};
use crate::commands::common_command::{ScheduleInfo, CredentialInfo};
use crate::commands::enrollment_commands::{EnrollResultInfo, EnrollParam};
use crate::traits::auth_policy_manager::{AuthPolicyManagerRegistry, AuthPolicy};
use crate::traits::config_manager::ConfigManagerRegistry;
use crate::traits::crypto_engine::{CryptoEngineRegistry, KeyPair};
use crate::traits::edit_session_manager::EditSessionManagerRegistry;
use crate::traits::executor_manager::{ExecutorInfo, ExecutorManagerRegistry, ExecutorFilter};
use crate::traits::misc_manager::MiscManagerRegistry;
use crate::traits::time_keeper::TimeKeeperRegistry;
use crate::traits::user_db_manager::{CredentialFilter, UserDbManagerRegistry, Credential, Enrolled};
use crate::utils::attribute::{AttributeKey, Attribute};
use crate::utils::auth_token::AuthToken;
use crate::utils::message_codec::{MessageCodec, MessageSignParam};
use crate::traits::context_manager::ContextManagerRegistry;

const INVALID_SENSOR_HINT: u32 = 0;
const TOKEN_VALIDITY_PERIOD: u64 = 10 * 60 * 100;

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct ExecutorEnrollResult {
    result: i32,
    schedule_id: u64,
    template_id: TemplateId,
    auth_sub_type: i32,
    capability_level: AuthCapabilityLevel,
    freezing_time: i32,
    remain_attempts: i32,
    root_secret: [u8; ROOT_SECRET_LEN],
}

impl ExecutorEnrollResult {
    pub fn result(&self) -> i32 {
        self.result
    }

    pub fn schedule_id(&self) -> u64 {
        self.schedule_id
    }

    pub fn template_id(&self) -> TemplateId {
        self.template_id
    }

    pub fn auth_sub_type(&self) -> i32 {
        self.auth_sub_type
    }

    pub fn capability_level(&self) -> AuthCapabilityLevel {
        self.capability_level
    }

    pub fn freezing_time(&self) -> i32 {
        self.freezing_time
    }

    pub fn remain_attempts(&self) -> i32 {
        self.remain_attempts
    }

    pub fn root_secret(&self) -> &[u8; ROOT_SECRET_LEN] {
        &self.root_secret
    }

    pub fn is_success(&self) -> bool {
        self.result == ErrorCode::Success as i32
    }
}

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct EnrollSchedule {
    schedule_id: u64,
    template_id_list: Vec<TemplateId>,
    executor_info_list: Vec<ExecutorInfo>
}

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct BaseEnrollContext {
    user_id: i32,
    user_type: UserType,
    auth_type: AuthType,
    capability_level: AuthCapabilityLevel,
    enroll_schedule: EnrollSchedule,
}

impl BaseEnrollContext {
    pub fn new(enroll_param: &EnrollParam) -> Self {
        BaseEnrollContext {
            user_id: enroll_param.user_id,
            user_type: enroll_param.user_type,
            auth_type: enroll_param.auth_type,
            capability_level: AuthCapabilityLevel::MaxAcl,
            enroll_schedule: EnrollSchedule {
                schedule_id: 0,
                template_id_list: Vec::new(),
                executor_info_list: Vec::new(),
            }
        }
    }

    fn query_all_in_one_executor_list(&self, enroll_param: &EnrollParam) -> Result<Vec<ExecutorInfo>, ErrorCode> {
        let executor_matcher = self.get_executor_matcher(enroll_param)?;
        let local_udid = MiscManagerRegistry::get().get_udid()?;
        let auth_type = enroll_param.auth_type;
        let sensor_hint = enroll_param.executor_sensor_hint;
        let filter: ExecutorFilter = if enroll_param.executor_sensor_hint != INVALID_SENSOR_HINT {
            Box::new(move |executor: &ExecutorInfo| {
                executor.sensor_hint == sensor_hint &&
                executor.auth_type == auth_type &&
                executor.matcher == executor_matcher &&
                executor.device_udid == local_udid &&
                executor.role == ExecutorRole::AllInOne
            })
        } else {
            Box::new(move |executor: &ExecutorInfo| {
                executor.auth_type == auth_type &&
                executor.device_udid == local_udid &&
                executor.role == ExecutorRole::AllInOne
            })
        };

        let executor_info = ExecutorManagerRegistry::get().get_executor_list(filter);
        Ok(executor_info)
    }

    fn query_collector_executor_list(&self, enroll_param: &EnrollParam) -> Result<Vec<ExecutorInfo>, ErrorCode> {
        let executor_matcher = self.get_executor_matcher(enroll_param)?;
        let collector_udid = MiscManagerRegistry::get().get_udid()?;
        let auth_type = enroll_param.auth_type;
        let sensor_hint = enroll_param.executor_sensor_hint;
        let filter: ExecutorFilter = if enroll_param.executor_sensor_hint != INVALID_SENSOR_HINT {
            Box::new(move |executor: &ExecutorInfo| {
                executor.sensor_hint == sensor_hint &&
                executor.auth_type == auth_type &&
                executor.device_udid == collector_udid &&
                executor.matcher == executor_matcher &&
                executor.role == ExecutorRole::Collector
            })
        } else {
            Box::new(move |executor: &ExecutorInfo| {
                executor.auth_type == auth_type &&
                executor.device_udid == collector_udid &&
                executor.role == ExecutorRole::Collector
            })
        };

        let executor_info = ExecutorManagerRegistry::get().get_executor_list(filter);
        Ok(executor_info)
    }

    fn query_executor_list(&self, enroll_param: &EnrollParam) -> Result<Vec<ExecutorInfo>, ErrorCode> {
        let all_in_one_executors = self.query_all_in_one_executor_list(enroll_param)?;
        let collector_executors = self.query_collector_executor_list(enroll_param)?;

        let mut result = all_in_one_executors;
        result.extend(collector_executors);
        Ok(result)
    }

    fn query_template_id_list(&self, filter: CredentialFilter) -> Result<Vec<TemplateId>, ErrorCode> {
        let credential_list = UserDbManagerRegistry::get().get_credential_list(filter)?;
        let template_id_list = credential_list
            .iter()
            .map(|cred| cred.template_id)
            .collect::<Vec<TemplateId>>();
        Ok(template_id_list)
    }

    pub fn get_user_id(&self) -> i32 {
        self.user_id
    }

    pub fn get_user_type(&self) -> UserType {
        self.user_type
    }

    pub fn get_auth_type(&self) -> AuthType {
        self.auth_type
    }

    pub fn get_capability_level(&self) -> AuthCapabilityLevel {
        self.capability_level
    }

    pub fn get_enroll_schedule(&self) -> &EnrollSchedule {
        &self.enroll_schedule
    }

    pub fn get_schedule_id(&self) -> u64 {
        self.enroll_schedule.schedule_id
    }

    pub fn get_template_id_list(&self) -> &Vec<TemplateId> {
        &self.enroll_schedule.template_id_list
    }

    pub fn get_executor_info_list(&self) -> &Vec<ExecutorInfo> {
        &self.enroll_schedule.executor_info_list
    }

    pub fn check_operate_permission(&self, enroll_param: &EnrollParam) -> Result<(), ErrorCode> {
        let enable_status = ConfigManagerRegistry::get().get_enable_status(enroll_param.auth_type,
            enroll_param.user_id)?;
        if !enable_status {
            log_e!("authtype {:?} is not support", enroll_param.auth_type);
            return Err(ErrorCode::TypeNotSupport);
        }

        EditSessionManagerRegistry::get_mut().check_session_valid(enroll_param.user_id)?;

        let user_valid = UserDbManagerRegistry::get().check_user_type_valid(enroll_param.user_type)?;
        if !user_valid {
            log_e!("user type {:?} is not valid", enroll_param.user_type);
            return Err(ErrorCode::GeneralError);
        }

        UserDbManagerRegistry::get().check_specification(enroll_param.user_id,
            enroll_param.auth_type, ScheduleType::Enroll)?;
        Ok(())
    }

    pub fn get_executor_matcher(&self, enroll_param: &EnrollParam) -> Result<u32, ErrorCode> {
        let auth_type = enroll_param.auth_type;
        let sensor_hint = enroll_param.executor_sensor_hint;
        let filter: ExecutorFilter = Box::new(move |executor: &ExecutorInfo| {
            executor.auth_type == auth_type &&
            executor.sensor_hint == sensor_hint &&
            (executor.role == ExecutorRole::Collector ||
            executor.role == ExecutorRole::AllInOne)
        });

        ExecutorManagerRegistry::get().get_executor_matcher(filter)
    }

    pub fn get_executor_indexes(&self, enroll_param: &EnrollParam) -> Result<Vec<u64>, ErrorCode> {
        let executor_list = self.query_executor_list(enroll_param)?;
        let indexes = executor_list
            .into_iter()
            .map(|executor_info| executor_info.executor_index)
            .collect();
        Ok(indexes)
    }

    pub fn generate_enroll_schedule(&mut self, enroll_param: &EnrollParam, filter: CredentialFilter) -> Result<(), ErrorCode> {
        self.enroll_schedule.schedule_id = ContextManagerRegistry::get().generate_unique_schedule_id()?;
        self.enroll_schedule.template_id_list = self.query_template_id_list(filter)?;
        self.enroll_schedule.executor_info_list = self.query_executor_list(enroll_param)?;
        Ok(())
    }

    pub fn create_executor_message(&self, enroll_schedule: &EnrollSchedule) -> Result<Vec<u8>, ErrorCode> {
        let template_ids: Vec<u64> = enroll_schedule.template_id_list.iter()
            .map(|&template_id| template_id.0)
            .collect();

        let mut attribute = Attribute::new();
        attribute.set_u64_slice(AttributeKey::AttrTemplateIdList, template_ids.as_slice());
        attribute.set_u32(AttributeKey::AttrUserId, self.user_id as u32);
        attribute.set_u64(AttributeKey::AttrScheduleId, enroll_schedule.schedule_id);

        // todo:
        let empty_keypair = KeyPair {
            pub_key: Vec::new(),
            pri_key: Vec::new(),
        };
        let message_codec = MessageCodec::new(MessageSignParam::Executor(empty_keypair));

        let executor_messages = message_codec.serialize_attribute(&attribute)?;
        Ok(executor_messages)
    }

    pub fn parse_executor_message(&self, executor_message: &[u8]) -> Result<ExecutorEnrollResult, ErrorCode> {
        let empty_keypair = KeyPair {
            pub_key: Vec::new(),
            pri_key: Vec::new(),
        };

        let message_codec = MessageCodec::new(MessageSignParam::Executor(empty_keypair));
        let attribute = message_codec.deserialize_attribute(executor_message)?;

        let result = attribute.get_i32(AttributeKey::AttrResultCode)?;
        let schedule_id = attribute.get_u64(AttributeKey::AttrScheduleId)?;
        let template_id = TemplateId(attribute.get_u64(AttributeKey::AttrTemplateId)?);
        let pin_sub_type = attribute.get_i32(AttributeKey::AttrPinSubType)?;
        let capability_level_value = attribute.get_u32(AttributeKey::AttrCapabilityLevel)?;
        let capability_level = AuthCapabilityLevel::try_from(capability_level_value)
            .map_err(|_| ErrorCode::GeneralError)?;
        let remain_attempts = attribute.get_i32(AttributeKey::AttrRemainAttempts)?;
        let freezing_time = attribute.get_i32(AttributeKey::AttrLockoutDuration)?;
        let mut root_secret: [u8; ROOT_SECRET_LEN] = [0; ROOT_SECRET_LEN];
        attribute.fill_u8_slice(AttributeKey::AttrRootSecret, &mut root_secret)?;

        let enroll_result_info = ExecutorEnrollResult {
            result,
            schedule_id,
            template_id,
            auth_sub_type: pin_sub_type,
            capability_level,
            freezing_time,
            remain_attempts,
            root_secret,
        };

        Ok(enroll_result_info)
    }

    pub fn add_credential(&mut self, executor_result: &ExecutorEnrollResult) -> Result<EnrollResultInfo, ErrorCode> {
        log_i!("add_credential start");
        let credential_id = UserDbManagerRegistry::get().generate_unique_credential_id()
            .map_err(|_| {
                log_e!("Failed to generate credential id");
                ErrorCode::GeneralError
            })?;

        let credential_info = Credential {
            user_id: self.user_id,
            credential_id: CredentialId(credential_id),
            template_id: executor_result.template_id,
            auth_type: self.auth_type,
            executor_sensor_hint: self.enroll_schedule.executor_info_list.first()
                .map(|exec| exec.sensor_hint)
                .unwrap_or(INVALID_SENSOR_HINT),
            executor_matcher: self.enroll_schedule.executor_info_list.first()
                .map(|exec| exec.matcher)
                .unwrap_or(0),
            capability_level: executor_result.capability_level,
            credential_type: CredentialType::try_from(executor_result.auth_sub_type as i32)?,
            enrolled_sys_time: TimeKeeperRegistry::get().get_ree_time()?,
            abandon_sys_time: 0,
            abandon_flag: false,
            storage_type: CredStorageType::StorageTypeTee,
        };
        match UserDbManagerRegistry::get().get_user(self.user_id) {
            Ok(_) => {
                log_i!("User is not exist, creating new user");
            }
            Err(ErrorCode::NotFound) => {
                log_i!("User is not found, creating new user");
                UserDbManagerRegistry::get_mut().add_user(self.user_id, self.user_type)?;
            }
            Err(e) => {
                // Other error, propagate it
                return Err(e);
            }
        }
        UserDbManagerRegistry::get_mut().add_credential(self.user_id, &credential_info)?;

        let enrolled_id = UserDbManagerRegistry::get().generate_unique_enrolled_id()
            .map_err(|_| {
                log_e!("Failed to generate enrolled id");
                ErrorCode::GeneralError
            })?;

        let enrolled_info = Enrolled {
            user_id: self.user_id,
            enrolled_id: enrolled_id,
            auth_type: self.auth_type,
        };
        UserDbManagerRegistry::get_mut().add_enrolled_id(self.user_id, &enrolled_info)?;
        let enroll_result_info = EnrollResultInfo {
            credential_id: CredentialId(credential_id),
            old_info: CredentialInfo::default(),
            root_secret: Some(executor_result.root_secret),
            old_root_secret: None,
            auth_token: Vec::new(),
        };
        Ok(enroll_result_info)
    }

    pub fn generate_enroll_token(&self, user_id: i32, credential_id: CredentialId) -> Result<Vec<u8>, ErrorCode> {
        let challenge = EditSessionManagerRegistry::get_mut().get_session_challenge(user_id)?;
        let time = TimeKeeperRegistry::get().get_system_time()?;
        let auth_trust_level = AuthTrustLevel::Atl3;
        let schedule_mode = ScheduleMode::Enroll;
        let security_level = AuthSecurityLevel::Asl0;
        let token_type = TokenType::LocalAuth;
        let secure_uid = UserDbManagerRegistry::get().get_sec_uid(user_id)?;
        let enrolled_id = UserDbManagerRegistry::get().get_enrolled_id(user_id, self.auth_type)?;
        let collector_udid = MiscManagerRegistry::get().get_udid()?;
        let verifier_udid = MiscManagerRegistry::get().get_udid()?;

        let mut origin_auth_token = AuthToken::new(
            challenge,
            time,
            auth_trust_level,
            self.auth_type,
            schedule_mode,
            security_level,
            token_type,
            user_id,
            secure_uid,
            enrolled_id,
            credential_id,
            collector_udid,
            verifier_udid,
        );

        let auth_token = origin_auth_token.encode_to_slice()?;
        Ok(auth_token)
    }

    pub fn check_cred_change_token(user_id: i32, auth_token: &[u8]) -> bool {
        let token = match AuthToken::decode_from_slice(auth_token, TOKEN_VALIDITY_PERIOD) {
            Err(_e) => return false,
            Ok(value) => value,
        };
        if token.auth_type != AuthType::Pin {
            log_e!("check authType:{} fail", token.auth_type as i32);
            return false;
        }
        if let Err(err) = EditSessionManagerRegistry::get_mut().check_challenge(user_id, token.challenge) {
            log_e!("check_challenge failed, ret::{}", err as i32);
            return false;
        }
        let user_info = match UserDbManagerRegistry::get().get_user(user_id) {
            Err(_e) => return false,
            Ok(value) => value,
        };
        if user_info.sec_uid != token.secure_uid {
            log_e!("mismatch secUid:{},{}", user_info.sec_uid, token.secure_uid);
            return false;
        }
        if token.schedule_mode != ScheduleMode::Auth && token.schedule_mode != ScheduleMode::Enroll &&
            token.schedule_mode != ScheduleMode::ReuseUnlockAuthResult {
            log_e!("bad schedule mode:{}", token.schedule_mode as i32);
            return false;
        }
        if token.token_type != TokenType::LocalAuth && token.token_type != TokenType::LocalResign {
            log_e!("bad token type:{}", token.token_type as i32);
            return false;
        }
        //todo
        return true;
    }
}
